﻿#include "../../includes/3d/cirrlichtwidget.h"
#include "../../includes/QsLog/QsLog.h"

#include <qDebug>
#include <QResizeEvent>
#include <QApplication>
#include <QGuiApplication>
#include <QMenu>

#ifdef ENABLE_IRRLICHT

#if defined (WIN32)
    //#pragma comment(lib, "E:/software/demo2/libqtcore/libqtcore/extends/irrlicht/libs/Irrlicht.lib")
#endif

CIrrlichtWidget::CIrrlichtWidget(QWidget *parent)
    : QFrame(parent),
      m_device(NULL),
      m_driver(NULL),
      m_smgr(NULL),
      m_guienv(NULL),
      m_isMouseInside(false)
{
    setWindowFlags(Qt::Window|Qt::FramelessWindowHint|Qt::WindowTitleHint);
    this->setObjectName("IrrlichtWidget");
    this->setMouseTracking(true);

    installEventFilter(this);

    irr::SIrrlichtCreationParameters param;
    param.DriverType = video::EDT_OPENGL;
    param.AntiAlias = 10;
    //param.IgnoreInput = true;
    param.WindowId = reinterpret_cast<void*>(this->winId());

    m_device = irr::createDeviceEx(param);
    if (m_device)
    {
        QLOG_INFO()<<"Irrlicht device create successed.";

        m_driver = m_device->getVideoDriver();
        m_smgr = m_device->getSceneManager();
        m_guienv = m_device->getGUIEnvironment();
    }
    else
    {
        QLOG_ERROR()<<"Irrlicht device create fail.";
    }  
}

CIrrlichtWidget::~CIrrlichtWidget()
{
    if(m_device)
    {
        m_device->closeDevice();
        m_device->drop();

        QLOG_INFO()<<"Irrlicht device close successed.";
    }
}

bool CIrrlichtWidget::loadResources(void)
{
    return true;
}

void CIrrlichtWidget::render(void)
{
    if (m_device == NULL || !m_device->run())
        return;

    m_driver->beginScene(true, true, SColor(255,0,0,0));

    m_smgr->drawAll();
    m_guienv->drawAll();

    m_driver->endScene();
}

void CIrrlichtWidget::start(int msec)
{
    if(this->loadResources())
        QLOG_INFO()<<"loadResources successed.";
    else
        QLOG_INFO()<<"loadResources fail.";

    m_rendertimer.start(msec, this);

    if(this->isHidden()) this->show();
}

void CIrrlichtWidget::timerEvent(QTimerEvent *)
{
    render();
}

/**
 * @brief CIrrlichtWidget::onProcessResizeEvent 处理窗口大小改变事件
 * @param event
 */
void CIrrlichtWidget::onProcessResizeEvent(QResizeEvent *event)
{
    core::dimension2d<u32> widgetSize;
    widgetSize.Width = event->size().width();
    widgetSize.Height = event->size().height();

    m_device->getVideoDriver()->OnResize(widgetSize);

    scene::ICameraSceneNode *cam = m_device->getSceneManager()->getActiveCamera();
    if(cam != NULL) cam->setAspectRatio((f32)widgetSize.Width/(f32)widgetSize.Height);
}

void CIrrlichtWidget::IrrMouseEvent(QMouseEvent *e,EMOUSE_INPUT_EVENT eventtype)
{
    if(m_device == NULL || e == NULL)
        return;

    QPoint newpos = ChangeScreenToLocalPos(e);

    SEvent irrEvent;
    irrEvent.EventType = EET_MOUSE_INPUT_EVENT;
    irrEvent.MouseInput.Event = eventtype;
    irrEvent.MouseInput.X = newpos.x();
    irrEvent.MouseInput.Y = newpos.y();
    irrEvent.MouseInput.Wheel = 0.0f;

    m_device->postEventFromUser(irrEvent);
}

/**
 * @brief CIrrlichtWidget::IsInsidePoint 判断指定的坐标是否在当前控件内
 * @param pos 要判断的坐标
 * @return 如果坐标在当前控件内返回真，否则返回假
 */
bool CIrrlichtWidget::IsInsidePoint(QMouseEvent *event)
{
    QRect pDecRect = QRect(this->mapToGlobal(this->rect().topLeft()),
                           this->rect().size());

    if(pDecRect.contains(event->globalPos()))
        return true;

    return false;
}

/**
 * @brief CIrrlichtWidget::ChangeScreenToLocalPos 将屏幕坐标转换为当前控件内坐标
 * @param event
 * @return
 */
QPoint CIrrlichtWidget::ChangeScreenToLocalPos(QMouseEvent *event)
{
    QRect pDecRect = QRect(this->mapToGlobal(this->rect().topLeft()),
                           this->rect().size());

    return QPoint(event->globalPos().x()-pDecRect.left(),
                  event->globalPos().y()-pDecRect.top());
}

/**
 * @brief CIrrlichtWidget::onProcessWheelEvent 处理鼠标滚轮事件
 * @param event
 */
void CIrrlichtWidget::onProcessWheelEvent(QWheelEvent *event)
{

}

bool CIrrlichtWidget::eventFilter(QObject *watched, QEvent *event)
{
    if(watched == this)
    {
        switch (event->type())
        {
            case QEvent::MouseButtonPress:
            case QEvent::MouseButtonRelease:
            case QEvent::MouseMove:
            {
                onProcessMouseEvent(static_cast<QMouseEvent*>(event));
            }
                break;
            case QEvent::Wheel:
            {
                onProcessWheelEvent(static_cast<QWheelEvent*>(event));
            }
                break;
            case QEvent::Resize:
            {
                onProcessResizeEvent(static_cast<QResizeEvent*>(event));
            }
                break;
            default:
                break;
        }
    }

    return QFrame::eventFilter(watched,event);
}

QMainWindow* CIrrlichtWidget::getMainWindow(void)
{
    foreach (QWidget *w, qApp->topLevelWidgets()) {
        if(QMainWindow* mainWin = qobject_cast<QMainWindow*>(w))
            return mainWin;
    }

    return nullptr;
}

/**
 * @brief onProcessMouseEvent 处理鼠标事件
 * @param event
 */
bool CIrrlichtWidget::onProcessMouseEvent(QMouseEvent *event)
{
    if(event == NULL || !IsInsidePoint(event))
    {
        if(event &&
            (event->type() == QEvent::MouseButtonPress ||
             event->type() == QEvent::MouseButtonRelease ||
             event->type() == QEvent::MouseMove))
        {
            QLOG_DEBUG()<<"CIrrlichtWidget::onProcessMouseEvent outside.";

            //这个地方要说明一下，菜单显示和隐藏的目的是让主窗口重新获取事件消息，
            //如果不这么做，主窗口可能会卡死
            m_hideMenu.show();
            m_hideMenu.hide();

            QMainWindow *pMainWindow = getMainWindow();
            if(pMainWindow && qApp->activeWindow() != pMainWindow)
                pMainWindow->activateWindow();
        }

        return false;
    }

    if(event->type() == QEvent::MouseButtonPress)
    {
        if(!m_hideMenu.isHidden())
            m_hideMenu.hide();

        if(event->button() == Qt::LeftButton)
        {
            IrrMouseEvent(event,EMIE_LMOUSE_PRESSED_DOWN);
        }
        else if(event->button() == Qt::RightButton)
            IrrMouseEvent(event,EMIE_RMOUSE_PRESSED_DOWN);
    }
    else if(event->type() == QEvent::MouseButtonRelease)
    {
        if(event->button() == Qt::LeftButton)
            IrrMouseEvent(event,EMIE_LMOUSE_LEFT_UP);
        else if(event->button() == Qt::RightButton)
            IrrMouseEvent(event,EMIE_RMOUSE_LEFT_UP);
    }
    else if(event->type() == QEvent::MouseMove)
    {
        IrrMouseEvent(event,EMIE_MOUSE_MOVED);
    }

    return true;
}

#endif
